package com.alks.function.service.impl.finance.mtlcosts;

import com.alks.common.aop.AutoPageAop;
import com.alks.common.data.dto.SysUserTokenDTO;
import com.alks.common.data.thread.UserIdThread;
import com.alks.common.utils.DateUtils;
import com.alks.common.utils.beanutils.ArrayUtils;
import com.alks.common.utils.beanutils.BeanUtil;
import com.alks.common.utils.beanutils.StringUtils;
import com.alks.common.utils.number.AmtUtils;
import com.alks.entity.data.entity.PcLastStatus2;
import com.alks.entity.data.entity.PcLotHead;
import com.alks.entity.data.entity.PcOutListDetail;
import com.alks.entity.data.entity.PcOutListDetailSize;
import com.alks.entity.data.response.ResponseInfo;
import com.alks.entity.exception.InDataException;
import com.alks.entity.exception.ServiceErrorException;
import com.alks.function.controller.finance.costcheck.LotMtlUsgPriceController;
import com.alks.function.data.dto.finance.mtlcosts.OutListDetailDto;
import com.alks.function.data.dto.finance.mtlcosts.OutListTempDto;
import com.alks.function.data.request.finance.mtlcosts.OutCfmRequest;
import com.alks.function.data.request.finance.mtlcosts.OutListDetailRequest;
import com.alks.function.data.request.finance.mtlcosts.OutListSizeFindRequest;
import com.alks.function.data.request.finance.mtlcosts.OutListTempRequest;
import com.alks.function.mapper.chengKong.PcLotHeadMapper;
import com.alks.function.mapper.finance.fincons.PcLastStatus2Mapper;
import com.alks.function.mapper.finance.mtlcosts.PcOutListDetailMapper;
import com.alks.function.mapper.finance.mtlcosts.PcOutListDetailSizeMapper;
import com.alks.function.service.finance.mtlcosts.IOutListFinService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author MS
 * @since 2024/6/25下午3:26
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class OutListFinServiceImpl extends ServiceImpl<PcOutListDetailMapper, PcOutListDetail> implements IOutListFinService {
    private final PcOutListDetailMapper detailMapper;
    private final PcOutListDetailSizeMapper sizeMapper;
    private final PcLastStatus2Mapper lastMapper;
    private final PcLotHeadMapper lotHeadMapper;
    private final Integer unCfm = 0;
    private final Integer cfm = 1;
    private final LotMtlUsgPriceController lot;

    @Override
    @AutoPageAop
    public ResponseInfo outListTempQuery(OutListTempRequest request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<PcOutListDetail> details = lambdaQuery()
                .select(PcOutListDetail::getSlipNo, PcOutListDetail::getPurDate, PcOutListDetail::getFinanceYymm, PcOutListDetail::getCfmFlag)
                .like(StringUtil.isNotBlank(request.getSlipNo()), PcOutListDetail::getSlipNo, request.getSlipNo())
                .like(StringUtil.isNotBlank(request.getLotNo()), PcOutListDetail::getLotNo, request.getLotNo())
                .like(StringUtil.isNotBlank(request.getVendor()), PcOutListDetail::getVendor, request.getVendor())
                .eq(StringUtil.isNotBlank(request.getFinanceYymm()), PcOutListDetail::getFinanceYymm, request.getFinanceYymm())
                .eq(cfm.equals(request.getCfmType()), PcOutListDetail::getCfmFlag, "Y")
                .isNull(unCfm.equals(request.getCfmType()), PcOutListDetail::getCfmFlag)
                .eq(PcOutListDetail::getCompanyId, companyId)
                .groupBy(PcOutListDetail::getSlipNo,PcOutListDetail::getPurDate,PcOutListDetail::getFinanceYymm)
                .last("ORDER BY SLIP_NO DESC,LOT_NO DESC").list();
        List<OutListTempDto> dtos = BeanUtil.copyList(details, OutListTempDto.class);
        Page<PcOutListDetail> page = (Page<PcOutListDetail>) details;
        /*封装返回*/
        Map<String, Object> map = new HashMap<>(2);
        map.put("dtos", dtos);
        map.put("total", page.getTotal());
        return ResponseInfo.ok(map);
    }

    @Override
    @AutoPageAop
    public Map<String, Object> outListDetailQuery(OutListDetailRequest request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<OutListDetailDto> dtos = detailMapper.outListDetailQuery(request, companyId);
        /*获取求和*/
        Map<String, BigDecimal> sumMap = detailMapper.outListDetailSumQuery(request, companyId);
        /*封装数据*/
        Map<String, Object> map = new HashMap<>(3);
        map.put("dtos", dtos);
        map.put("sum", sumMap);
        if (!request.getPageNum().equals(-1) && !request.getPageSize().equals(-1)) {
            Page<OutListDetailDto> page = (Page<OutListDetailDto>) dtos;
            map.put("total", page.getTotal());
        }
        return map;
    }

    @Override
    public ResponseInfo outListSizeQuery(OutListSizeFindRequest request) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        LambdaQueryWrapper<PcOutListDetailSize> wrapper = newSizeWrapper(request, companyId)
                .eq(PcOutListDetailSize::getReqNo, request.getReqNo())
                .eq(StringUtil.isNotBlank(request.getPartName()),PcOutListDetailSize::getPartName,request.getPartName())
                .isNull(StringUtil.isBlank(request.getPartName()),PcOutListDetailSize::getPartName)
                .eq(StringUtil.isNotBlank(request.getLotNo()),PcOutListDetailSize::getLotNo,request.getLotNo())
                .isNull(StringUtil.isBlank(request.getLotNo()),PcOutListDetailSize::getLotNo)
                .eq(StringUtil.isNotBlank(request.getProcess()),PcOutListDetailSize::getProcess,request.getProcess())
                .isNull(StringUtil.isBlank(request.getProcess()),PcOutListDetailSize::getProcess);
        List<PcOutListDetailSize> sizes = sizeMapper.selectList(wrapper);
        if (ArrayUtils.isEmpyt(sizes)) {
            /*老数据存在丢失SLIP_NO的情况*/
            LambdaQueryWrapper<PcOutListDetailSize> wrapper1 = newSizeWrapper(request, companyId)
                    .isNull(PcOutListDetailSize::getSlipNo);
            sizes = sizeMapper.selectList(wrapper1);
        }
        /*处理数据*/
        List<Map<String, Object>> dtos = new ArrayList<>();
        for (PcOutListDetailSize size : sizes) {
            Map<String, Object> sizeMap = new HashMap<>(2);
            sizeMap.put("sizeNo", size.getSizeNo());
            sizeMap.put("qty", size.getQty());
            dtos.add(sizeMap);
        }
        return ResponseInfo.ok(dtos);
    }

    /**
     * 快速创建需要的sizeWrapper
     *
     * @param request   查询条件
     * @param companyId 公司ID
     * @return 创建好的wrapper
     */
    private static LambdaQueryWrapper<PcOutListDetailSize> newSizeWrapper(OutListSizeFindRequest request, String companyId) {
        return new LambdaQueryWrapper<PcOutListDetailSize>()
                .select(PcOutListDetailSize::getSizeNo, PcOutListDetailSize::getQty)
                .eq(PcOutListDetailSize::getReqNo, request.getReqNo())
                .eq(PcOutListDetailSize::getLotNo, request.getLotNo())
                .eq(PcOutListDetailSize::getPartName, request.getPartName())
                .eq(StringUtil.isNotBlank(request.getProcess()), PcOutListDetailSize::getProcess, request.getProcess())
                .eq(PcOutListDetailSize::getCompanyId, companyId)
                .last("ORDER BY SLIP_NO DESC , LOT_NO DESC");
    }

    @Override
    public ResponseInfo outListTempCfm(List<OutCfmRequest> request, Integer type, String financeYymm, String accountName, Integer retryFlag) {
        SysUserTokenDTO tokenDTO = UserIdThread.get();
        String companyId = tokenDTO.getCompanyId();
        String userName = tokenDTO.getUserName();
        /*检核入参检验*/
        if (!cfm.equals(type) && !unCfm.equals(type)){
            log.error("\n错误接口：outListTempCfm\n错误原因：传参错误\n要求传参：0 取消 1 检核 || 实际传参：{}",type);
            throw new InDataException("传参异常");
        }
        List<String> idList = request.stream().map(OutCfmRequest::getId).filter(Objects::nonNull).collect(Collectors.toList());
        if (type.equals(cfm)) {
            List<String> cfmSlipNoList = request.stream().filter(a->StringUtil.isNotBlank(a.getFinanceYymm())).map(OutCfmRequest::getSlipNo).collect(Collectors.toList());
            if (ArrayUtils.isNoEmpyt(cfmSlipNoList)){
                throw new InDataException("已检核数据不能重复检核");
            }if (StringUtils.isBlank(accountName)) {
                throw new InDataException("结算方式不能为空哦");
            }
            DateUtils.financeYymmCheck(financeYymm);
        }if(ArrayUtils.isEmpyt(idList)&&type.equals(unCfm)){
            List<String> unCfmSlipNoList = request.stream().filter(a->StringUtil.isBlank(a.getFinanceYymm())).map(OutCfmRequest::getSlipNo).collect(Collectors.toList());
            if (ArrayUtils.isNoEmpyt(unCfmSlipNoList)){
                throw new InDataException("未检核数据不能取消检核");
            }
        }
        /*区分单条检核和整单检核*/
        if (ArrayUtils.isEmpyt(idList)) {
            List<String> slipList = request.stream().map(OutCfmRequest::getSlipNo).filter(Objects::nonNull).collect(Collectors.toList());
            if (ArrayUtils.isEmpyt(slipList)){
                throw new InDataException("请选择要检核的数据");
            }
        }
        LambdaUpdateWrapper<PcOutListDetail> wrapper = new LambdaUpdateWrapper<PcOutListDetail>()
                .in(ArrayUtils.isNoEmpyt(idList),PcOutListDetail::getId, idList)
                .eq(PcOutListDetail::getCompanyId, companyId);
        /*补充根据订单和结算号码选择数据*/
        if (ArrayUtils.isEmpyt(idList)) {
            wrapper.and(w -> {
            boolean flag = false;
            for (OutCfmRequest r : request) {
                if (flag) {
                    w.or(a -> a.eq(PcOutListDetail::getSlipNo, r.getSlipNo())
                            .eq(StringUtil.isNotBlank(r.getFinanceYymm()), PcOutListDetail::getFinanceYymm, r.getFinanceYymm())
                            .isNull(StringUtil.isBlank(r.getFinanceYymm()), PcOutListDetail::getFinanceYymm)
                            .ge(PcOutListDetail::getPurDate, r.getPurDate())
                            .le(PcOutListDetail::getPurDate, r.getPurDate().plusDays(1)));
                } else {
                    w.and(a -> a.eq(PcOutListDetail::getSlipNo, r.getSlipNo())
                            .eq(StringUtil.isNotBlank(r.getFinanceYymm()), PcOutListDetail::getFinanceYymm, r.getFinanceYymm())
                            .isNull(StringUtil.isBlank(r.getFinanceYymm()), PcOutListDetail::getFinanceYymm)
                            .ge(PcOutListDetail::getPurDate, r.getPurDate())
                            .le(PcOutListDetail::getPurDate, r.getPurDate().plusDays(1)));
                }
                flag = true;
            }
            return w;
            });
        }
        List<PcOutListDetail> details = detailMapper.selectList(wrapper);
        if (ArrayUtils.isEmpyt(details)){
            log.error("\n错误接口：outListTempCfm\n错误原因：无法找到指定数据\n对应表：【PC_OUT_LIST_DETAIL】\n入参：{}",request);
            String msg = cfm.equals(type) ? "检核" : "取消检核";
            throw new ServiceErrorException("暂无可"+msg+"数据");
        }
        /*已有结算单号不能取消检核*/
        List<String> financeDetails = details.stream()
                .filter(Objects::nonNull)
                .filter(detail -> StringUtil.isNotBlank(detail.getBalanceNo()))
                .map(PcOutListDetail::getSlipNo)
                .distinct()
                .collect(Collectors.toList());
        if (ArrayUtils.isNoEmpyt(financeDetails)) {
            throw new ServiceErrorException("存在包含" + financeDetails.get(0) + "在内" + financeDetails.size() + "条数据已应付,无法取消检核");
        }
        /*外发数大于订单数警告*/
        if (type.equals(1) && !retryFlag.equals(1)){
            List<String> lotNoSet = details.stream()
                    .map(PcOutListDetail::getLotNo)
                    .distinct().filter(Objects::nonNull)
                    .collect(Collectors.toList());
            List<PcLotHead> lotHeads = lotHeadMapper.selectByLotNos(lotNoSet, companyId);
            if (ArrayUtils.isEmpyt(lotHeads)){
                throw new ServiceErrorException("指令单不存在");
            }
            Map<String, Float> lotQtyMap = lotHeads.stream().collect(Collectors.toMap(PcLotHead::getLotNo, a -> a.getOrderQty() + a.getSurplusQty()));
            for (PcOutListDetail detail : details) {
                BigDecimal orderQty = new BigDecimal(lotQtyMap.get(detail.getLotNo()).toString());
                if (orderQty.compareTo(detail.getPurQty()) < 0) {
                    return ResponseInfo.retry("存在订单号" + detail.getSlipNo() + "的外发数大于指令单" + detail.getLotNo() + "的订单数,请确认");
                }
            }
        }
        /*成型未完成不能检核*/
        List<String> lotNoList = details.stream()
                .filter(Objects::nonNull)
                .map(PcOutListDetail::getLotNo)
                .distinct()
                .collect(Collectors.toList());
        /*不能重复检核/取消检核*/
        if (cfm.equals(type)) {
            List<String> cfmDetails = details.stream().filter(Objects::nonNull)
                    .filter(detail -> "Y".equals(detail.getCfmFlag()))
                    .map(PcOutListDetail::getSlipNo)
                    .distinct()
                    .collect(Collectors.toList());
            if (ArrayUtils.isNoEmpyt(cfmDetails)) {
                throw new ServiceErrorException("存在包含订单号" + cfmDetails.get(0) + "在内" + cfmDetails.size() + "条数据已检核,请勿重复检核");
            }
            List<PcLastStatus2> unFinList = lastMapper.getUnFinByLotNo(lotNoList,companyId);
            if (ArrayUtils.isEmpyt(unFinList)){
                throw new ServiceErrorException("检核失败：暂无成型数据");
            }
            Set<String> lastFindLotNoList = unFinList.stream()
                    .map(PcLastStatus2::getLotNo)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            List<String> missLotNoList = ArrayUtils.findMiss(lotNoList, lastFindLotNoList);
            if (ArrayUtils.isNoEmpyt(missLotNoList)){
                throw new ServiceErrorException("检核失败：存在指令：" + missLotNoList.get(0) + "在内" + missLotNoList.size() + "条数据暂无成型记录");
            }
            List<String> unFinLotNoList = unFinList.stream()
                    .filter(a -> a.getOrdQty() > a.getProdQty())
                    .map(PcLastStatus2::getLotNo)
                    .filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.toList());
            if (ArrayUtils.isNoEmpyt(unFinLotNoList)){
                throw new ServiceErrorException("检核失败：存在指令：" + unFinLotNoList.get(0) + "在内" + unFinLotNoList.size() + "条数据未全部成型");
            }
            wrapper.set(PcOutListDetail::getCfmFlag, "Y")
                    .set(PcOutListDetail::getCfmUser, userName)
                    .set(PcOutListDetail::getCfmDate, LocalDateTime.now())
                    .set(PcOutListDetail::getFinanceYymm, financeYymm)
                    .set(PcOutListDetail::getAccountName, accountName);
        } else {
            List<String> unCfmDetails = details.stream().filter(Objects::nonNull)
                    .filter(detail -> StringUtils.isBlank(detail.getCfmFlag()))
                    .map(PcOutListDetail::getSlipNo)
                    .distinct()
                    .collect(Collectors.toList());
            if (ArrayUtils.isNoEmpyt(unCfmDetails)) {
                throw new ServiceErrorException("存在包含" + unCfmDetails.get(0) + "在内" + unCfmDetails.size() + "条数据未检核");
            }
            wrapper.set(PcOutListDetail::getCfmFlag, null)
                    .set(PcOutListDetail::getCfmUser, null)
                    .set(PcOutListDetail::getCfmDate, null)
                    .set(PcOutListDetail::getFinanceYymm, null)
                    .set(PcOutListDetail::getAccountName, null);
        }
        /*更新数据*/
        detailMapper.update(null, wrapper);
        return ResponseInfo.ok();
    }

    @Override
    public Map<String, List<String>> outListDetailBack(String vendorSearch, String financeSearch) {
        String companyId = UserIdThread.get().getCompanyId();
        /*获取数据*/
        List<String> vendorList = detailMapper.exportVendorBack(vendorSearch,companyId);
        List<String> financeList = detailMapper.exportFinanceBack(financeSearch,companyId);
        Map<String,List<String>> map = new HashMap<>();
        map.put("vendor",vendorList);
        map.put("finance",financeList);
        return map;
    }

    @Override
    public Map<String, String> finExportDispose(List<OutListDetailDto> dtoList) {
        String userName = UserIdThread.get().getUserName();
        /*合并数据*/
        Map<String, List<OutListDetailDto>> collect = dtoList.stream().collect(Collectors.groupingBy(OutListDetailDto::getLotNo));
        dtoList.clear();
        collect.forEach((key, value) -> {
            OutListDetailDto dto = new OutListDetailDto();
            dto.setLotNo(key);
            String partName = value.stream()
                    .map(OutListDetailDto::getPartName)
                    .filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.joining(","));
            BigDecimal amt = BigDecimal.ZERO;
            BigDecimal price = BigDecimal.ZERO;
            for (OutListDetailDto v : value) {
                amt=amt.add(v.getAmt());
                price=price.add(v.getPrice());
                dto.setUnit(v.getUnit());
                dto.setStyle(v.getStyle());
                dto.setPurQty(v.getPurQty());
            }
            dto.setPartName(partName);
            dto.setAmt(amt);
            dto.setPrice(price);
            dtoList.add(dto);
        });
        /*补充合计*/
        BigDecimal amtSum = dtoList.stream().map(OutListDetailDto::getAmt).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        OutListDetailDto dto = new OutListDetailDto();
        dto.setAmt(amtSum);
        BigDecimal qtySum = dtoList.stream().map(OutListDetailDto::getOrdQty).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
        dto.setOrdQty(qtySum);
        dto.setUnit("合计");
        dtoList.add(dto);
        /*补充其他数据*/
        Map<String,String> otherMap = new HashMap<>(5);
        otherMap.put("userName",userName);
        otherMap.put("amt", AmtUtils.digitCapital(amtSum.doubleValue()));
        return otherMap;
    }
}
